﻿#region [===== Using =====]
using System;
using System.ComponentModel;
using System.Globalization;
using System.IO;
using System.Security.Permissions;
using System.Text;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using Microsoft.SharePoint.Workflow;
#endregion

namespace TPG.SharePoint.Workflow
{
  /// <summary>
  /// A special placeholder which loads the placeholder content based on
  /// metadata in the workflow definition.
  /// </summary>
  /// <remarks>
  /// <para>The placeholder loads the requested data defined in the metadata 
  /// section of the workflow definition. The metadata defines a set of 
  /// name / value pairs which can either indicate a control or static text.</para>
  /// <para>The format used by the placeholder is as follows:
  /// <code>{PageType}_{PlaceHolderType}{Location}{DataType}</code>
  /// For some placeholders the PlaceHolderType is optional.This results in 
  /// the following format:
  /// <code>{PageType}_{Location}{DataType}</code></para>
  /// <para>The PageType is determined by reading the <see cref="IWorkflowPage.WorkflowMetaData" /> 
  /// property.</para>
  /// <para>The PlaceHolderType is determined using the 
  /// <see cref="WorkflowControlPlaceHolder.PlaceHolderType" /> property. This property
  /// is set by the page which houses the placeholder. </para>
  /// <para>The Location is determined using the 
  /// <see cref="WorkflowControlPlaceHolder.Location" /> property. This property
  /// is set by the page which houses the placeholder. </para>
  /// <para>The DataType is determined using the 
  /// <see cref="WorkflowControlPlaceHolder.AllowedContent" /> property. This property
  /// is set by the page which houses the placeholder. </para>
  /// </remarks>
  [PersistChildren(false)]
  [ParseChildren(true)]
  [AspNetHostingPermission(SecurityAction.InheritanceDemand, Level = AspNetHostingPermissionLevel.Minimal)]
  [AspNetHostingPermission(SecurityAction.LinkDemand, Level = AspNetHostingPermissionLevel.Minimal)]
  [ToolboxItem(false)]
  public class WorkflowControlPlaceHolder
        : PlaceHolder, INamingContainer
  {
    #region [===== Fields =====]
    private Control _formControl;
    #endregion

    #region [===== Properties =====]
    /// <summary>
    /// Gets the hosted control.
    /// </summary>
    public Control Control
    {
      get { return _formControl; }
    }

    /// <summary>
    /// Gets or sets a value indicating if content in the placeholder 
    /// is required.
    /// </summary>
    [Browsable(true)]
    [DefaultValue(false)]
    public bool RequiredContent
    {
      get { return (bool?)ViewState["RequiredContent"] ?? false; }
      set { ViewState["RequiredContent"] = value; }
    }

    /// <summary>
    /// Gets or sets a value indicating the type of content allowed in 
    /// the placeholder.
    /// </summary>
    [Browsable(true)]
    [DefaultValue(WorkflowControlPlaceHolderType.Control | WorkflowControlPlaceHolderType.Text)]
    public WorkflowControlPlaceHolderType AllowedContent
    {
      get
      {
        return (WorkflowControlPlaceHolderType?)ViewState["AllowedContent"] ??
          WorkflowControlPlaceHolderType.Control | WorkflowControlPlaceHolderType.Text;
      }
      set { ViewState["AllowedContent"] = value; }
    }

    /// <summary>
    /// Gets or sets the location of the placeholder.
    /// </summary>
    [Browsable(true)]
    public string Location { get; set; }
    /// <summary>
    /// Gets or sets the type of the placeholder.
    /// </summary>
    public string PlaceHolderType { get; set; }

    /// <summary>
    /// Gets or sets a template with default content for the placeholder.
    /// </summary>
    [PersistenceMode(PersistenceMode.InnerProperty)]
    [TemplateContainer(typeof(WorkflowControlPlaceHolder))]
    [Browsable(false)]
    [DefaultValue((string)null)]
    public ITemplate DefaultContent
    {
      get;
      set;
    }

    internal IWorkflowPage WorkflowPage
    {
      get { return (IWorkflowPage)Page; }
    }
    #endregion

    #region [===== Internal methods =====]
    internal string RenderAsHtml()
    {
      using (StringWriter writer = new StringWriter(CultureInfo.InvariantCulture))
      {
        using (HtmlTextWriter htmlWriter = new HtmlTextWriter(writer))
        {
          bool initialVisible = this.Visible;
          this.Visible = true;
          this.RenderControl(htmlWriter);
          this.Visible = initialVisible;
        }

        return writer.ToString();
      }
    }
    #endregion

    #region [===== Protected methods =====]
    /// <summary>
    /// Initializes the placeholder by loading the hosted control from the 
    /// workflow metadata.
    /// </summary>
    /// <param name="e">An EventArgs containing event data.</param>
    protected override void OnInit(EventArgs e)
    {
      // verify the control is in a workflow page            
      IWorkflowPage workflowPage = this.Page as IWorkflowPage;
      if (workflowPage == null)
      {
        throw new WorkflowException("This control must be placed on a page implementing IWorkflowPage.");
      }

      OnCreatingControl(EventArgs.Empty);

      if ((this.AllowedContent & WorkflowControlPlaceHolderType.Control) ==
          WorkflowControlPlaceHolderType.Control)
      {
        string formUrl = WorkflowConfiguration.LookupMetadata(
            workflowPage.WorkflowTemplate, workflowPage.WorkflowMetaData.PageType,
            this.PlaceHolderType, this.Location,
            WorkflowConfiguration.DataTypeUrl, true);
        if (String.IsNullOrEmpty(formUrl) == false)
        {
          _formControl = CreateFormControl(formUrl);
        }
      }
      if (_formControl == null)
      {
        if ((this.AllowedContent & WorkflowControlPlaceHolderType.Text) == WorkflowControlPlaceHolderType.Text)
        {
          string text = WorkflowConfiguration.LookupMetadata(
              workflowPage.WorkflowTemplate, workflowPage.WorkflowMetaData.PageType,
              PlaceHolderType, Location, WorkflowConfiguration.DataTypeText, true);
          if (String.IsNullOrEmpty(text) == false)
          {
            _formControl = CreateTextControl(text);
          }
        }
      }

      if ((_formControl == null) && this.RequiredContent)
        throw new WorkflowException(string.Format(CultureInfo.CurrentCulture, "The control with ID '{0}' requires content.", this.ID));

      if (_formControl != null)
      {
        this.Controls.Add(_formControl);
      }
      else
      {
        if (DefaultContent != null)
          DefaultContent.InstantiateIn(this);
      }

      OnCreatedControl(EventArgs.Empty);
      base.OnInit(e);
    }

    /// <summary>
    /// Raises the <see cref="WorkflowControlPlaceHolder.CreatingControl" /> event.
    /// </summary>
    /// <param name="e">An <see cref="EventArgs" /> containing
    /// event data.</param>
    protected virtual void OnCreatingControl(EventArgs e)
    {
      if (CreatingControl != null)
      {
        CreatingControl(this, e);
      }
    }

    /// <summary>
    /// Raises the <see cref="WorkflowControlPlaceHolder.CreatedControl" /> event.
    /// </summary>
    /// <param name="e">An <see cref="EventArgs" /> containing
    /// event data.</param>
    protected virtual void OnCreatedControl(EventArgs e)
    {
      if (CreatedControl != null)
      {
        CreatedControl(this, e);
      }
    }
    /// <summary>
    /// Creates a control based on the control virtual path.
    /// </summary>
    /// <param name="formUrl">The virtual path to the control to load.</param>
    /// <returns>The loaded control.</returns>
    protected virtual Control CreateFormControl(string formUrl)
    {
      return WorkflowControl.CreateFormControl(this.Page, formUrl);
    }
    /// <summary>
    /// Creates a control displaying a static piece of text.
    /// </summary>
    /// <param name="text">The text to display in the control.</param>
    /// <returns>The loaded control.</returns>
    protected virtual Control CreateTextControl(string text)
    {
      Literal control = new Literal();
      control.Text = text;
      return control;
    }
    #endregion

    #region [===== Events =====]
    /// <summary>
    /// An event firing before the hosted control is created.
    /// </summary>
    public event EventHandler CreatingControl;
    /// <summary>
    /// An event firing after the hosted control was created.
    /// </summary>
    public event EventHandler CreatedControl;
    #endregion
  }
}
